<?php
# 文件名称:session.php
defined( '_SYS' ) or die( 'No direct script access allowed' );

class C_session
{
	/**
	 * 内部状态
	 * @var	string $_state one of 'active'|'expired'|'destroyed|'error'
	 * @getState()函数调用
	 */
	var	$_state	=	'active';
	/**
	 * 周期/秒
	 */
	var	$_expire	=	900;
	/**
	* 安全策略(共享session判断)
	*
	* Default values:
	*  - fix_browser	//通过浏览器判断
	*  - fix_adress		//通过地址判断
	*/
	var $_security = array( 'fix_adress' );
	/**
	* 强制安全套接层协议层通信
	*/
	var $_force_ssl = false;
	function C_session( $options = array() )
	{
		// 在PHP4中注册一个伪造的析构函数__destruct
		if (version_compare(PHP_VERSION, '5') == -1) {
			register_shutdown_function((array(&$this, '__destruct')));
		}
		
		//销毁所有通过session.auto_start创建的session
		if (session_id()) {
			session_unset();
			session_destroy();
		}
		//禁止跨页传递sessionid，保存在数据库中
		@ini_set('session.use_trans_sid', '0');		
		
		//设置存储和获取与会话关联的数据的处理器的名字
		@ini_set('session.save_handler', 'files');
		
		if($path = dirname(__FILE__).DS.'storage.php')
		{
			require_once $path;
			//if (!class_exists( $tableClass ))
			{
				@ini_set('session.save_handler', 'user');
				//create handler
				$this->_store =& C_sessionStorage::getInstance($options['store']);
			}
		}		
		// 设置额外的会话选项
		$this->_setOptions( $options );
		//设置cookie参数
		$this->_setCookieParams();
		//创建session
		$this->_start();
		//初始化session
		$this->_setCounter();
		$this->_setTimers();
		$this->_state =	'active';
		// 安全检测
		$this->_validate();
	}
	function __destruct() {
		$this->close();
	}
	/**
	 * 返回一个全局session变量的引用
	 * $session = &C_session::getInstance();
	 */
	function & getInstance($options)
	{
		static $instance;
		if (!is_object($instance)) {
			$instance = new C_session($options);
		}
		return $instance;
	}
    function getState() {
		return $this->_state;
	}
    function getExpire() {
		return $this->_expire;
    }
	
	/**
	 * 获得会话的标志，如果标志未设置就会产生一个
	 */
	function getToken($forceNew = false)
	{
		$token = $this->get( 'session.token' );
		//创建标志
		if( $token === null || $forceNew ) {
			$token	=	$this->_createToken( 12 );
			$this->set( 'session.token', $token );
		}
		return $token;
	}
	/**
	 * 确定是否存在标志在该会话的方法。如果不是会话将被置为过期 
	 */
	function hasToken($tCheck, $forceExpire = true)
	{
		// 检测token是否存在
		$tStored = $this->get( 'session.token' );
		if(($tStored !== $tCheck))
		{
			if($forceExpire) {
				$this->_state = 'expired';
			}
			return false;
		}
		return true;
	}
	//客户端保存session ID的cookie名称 默认PHPSESSID
	function getName()
	{
		if( $this->_state === 'destroyed' ) {
			return null;
		}
		return session_name();
	}
	/**
	 * 获取session id
	 *
	 */
	function getId()
	{
		if( $this->_state === 'destroyed' ) {
			return null;
		}
		return session_id();
	}
	/**
	* 检查该会话是否是当前被创建的 
	*/
	function isNew()
	{
		$counter = $this->get( 'session.counter' );
		if( $counter === 1 ) {
			return true;
		}
		return false;
	}
	 /**
	 * 从session取得数据 
	 */
	function &get($name, $default = null, $namespace = 'default')
	{
		$namespace = '__'.$namespace; //添加前缀
		if($this->_state !== 'active' && $this->_state !== 'expired') {
			$error = null;
			return $error;
		}
		if (isset($_SESSION[$namespace][$name])) {
			return $_SESSION[$namespace][$name];
		}
		return $default;
	}
	/**
	 * 设置数据到会话存储里 
	 */
	function set($name, $value, $namespace = 'default')
	{
		$namespace = '__'.$namespace; 
		if($this->_state !== 'active') {
			return null;
		}
		$old = isset($_SESSION[$namespace][$name]) ?  $_SESSION[$namespace][$name] : null;
		if (null === $value) {
			unset($_SESSION[$namespace][$name]);
		} else {
			$_SESSION[$namespace][$name] = $value;
		}
		return $old;
	}
	/**
	* 检查会话中是否存在数据 
	*/
	function has( $name, $namespace = 'default' )
	{
		$namespace = '__'.$namespace;
		if( $this->_state !== 'active' ) {
			return null;
		}
		return isset( $_SESSION[$namespace][$name] );
	}
	/**
	* 从存贮取消会话数据 
	*/
	function clear( $name, $namespace = 'default' )
	{
		$namespace = '__'.$namespace;
		if( $this->_state !== 'active' ) {
			return null;
		}
		$value	=	null;
		if( isset( $_SESSION[$namespace][$name] ) ) {
			$value	=	$_SESSION[$namespace][$name];
			unset( $_SESSION[$namespace][$name] );
		}
		return $value;
	}
	/**
	* 创建或恢复会话
	*/
	function _start()
	{
		if( $this->_state == 'restart' ) {
			session_id( $this->_createId() );
		}
		session_cache_limiter('none');
		session_start();
		// for IE 6.0
		header('P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"');
		return true;
	}

	/**
	 * 释放全部session变量 
	 */
	function destroy()
	{
		if( $this->_state === 'destroyed' ) {
			return true;
		}
		// 删除session cookie
		if (isset($_COOKIE[session_name()])) {
			setcookie(session_name(), '', time()-42000, '/');
		}
		session_unset();
		session_destroy();
		$this->_state = 'destroyed';
		return true;
	}
	/**
    * 重置过期的会话并锁定会话 
	*/
	function restart()
	{
		$this->destroy();
		if( $this->_state !==  'destroyed' ) {
			return false;
		}
		// 重新注册
		$this->_store->register();
		
		$this->_state	=   'restart';
		//重新产生id
		$id	=	$this->_createId( strlen( $this->getId() ) );
		session_id($id);
		$this->_start();
		$this->_state	=	'active';
		$this->_validate();
		$this->_setCounter();
		return true;
	}
	/**
	* 创建一个新的会话，并从旧的会话复制变量 
	*/
	function fork()
	{
		if( $this->_state !== 'active' ) {
			return false;
		}
		$values	= $_SESSION;
		$trans	=	@ini_get( 'session.use_trans_sid' );
		if( $trans ) {
			@ini_set( 'session.use_trans_sid', 0 );
		}
		$cookie	=	session_get_cookie_params();
		// 创建新session id
		$id	=	$this->_createId( strlen( $this->getId() ) );
		// 销毁
		session_destroy();
		$this->_store->register();
		
		@ini_set( 'session.use_trans_sid', $trans );
		session_set_cookie_params( $cookie['lifetime'], $cookie['path'], $cookie['domain'], $cookie['secure'] );
		session_id( $id );
		session_start();
		return true;
	}
	 /**
	 * 写入会话数据并且结束会话 
	 */
	function close() {
		session_write_close();
	}
	 /**
	 * 创建会话ID
	 */
	function _createId( )
	{
		$id = 0;
		while (strlen($id) < 32)  {
			$id .= mt_rand(0, mt_getrandmax());
		}
		$id	= md5( uniqid($id, true));
		return $id;
	}
	 /**
	 * 设置session cookie参数
	 */
	function _setCookieParams() {
		$cookie	=	session_get_cookie_params();
		if($this->_force_ssl) {
			$cookie['secure'] = true;
		}
		session_set_cookie_params( $cookie['lifetime'], $cookie['path'], $cookie['domain'], $cookie['secure'] );
	}
	/**
	* 创建一个标志字符串 
	*/
	function _createToken( $length = 32 )
	{
		static $chars	=	'0123456789abcdef';
		$max			=	strlen( $chars ) - 1;
		$token			=	'';
		$name 			=  session_name();
		for( $i = 0; $i < $length; ++$i ) {
			$token .=	$chars[ (rand( 0, $max )) ];
		}
		return md5($token.$name);
	}
	/**
	* 设置计数器的会话使用 
	*/
	function _setCounter()
	{
		$counter = $this->get( 'session.counter', 0 );
		++$counter;
		$this->set( 'session.counter', $counter );
		return true;
	}
	/**
	* 设置会话定时器 
	*/
	function _setTimers()
	{
		if( !$this->has( 'session.timer.start' ) )
		{
			$start	=	time();
			$this->set( 'session.timer.start' , $start );
			$this->set( 'session.timer.last'  , $start );
			$this->set( 'session.timer.now'   , $start );
		}
		$this->set( 'session.timer.last', $this->get( 'session.timer.now' ) );
		$this->set( 'session.timer.now', time() );
		return true;
	}
	/**
	* 设定额外的会话选项 
	*/
	function _setOptions( &$options )
	{
		if( isset( $options['name'] ) ) {
			session_name( md5($options['name']) );
		}
		if( isset( $options['id'] ) ) {
			session_id( $options['id'] );
		}
		if( isset( $options['expire'] ) ) {
			$this->_expire	=	$options['expire'];
		}
		if( isset( $options['security'] ) ) {
			$this->_security	=	explode( ',', $options['security'] );
		}
		if( isset( $options['force_ssl'] ) ) {
			$this->_force_ssl = (bool) $options['force_ssl'];
		}
		@ini_set('session.gc_maxlifetime', $this->_expire);
		
		return true;
	}
	/**
	* 基于安全，做一些检查 
	*
	*/
	function _validate( $restart = false )
	{
		if( $restart )
		{
			$this->_state	=	'active';
			$this->set( 'session.client.address'	, null );
			$this->set( 'session.client.forwarded'	, null );
			$this->set( 'session.client.browser'	, null );
			$this->set( 'session.token'				, null );
		}
		
		if( $this->_expire )
		{
			$curTime =	$this->get( 'session.timer.now' , 0  );
			$maxTime =	$this->get( 'session.timer.last', 0 ) +  $this->_expire;
			if( $maxTime < $curTime ) {
				$this->_state	=	'expired';
				return false;
			}
		}
		if( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
			$this->set( 'session.client.forwarded', $_SERVER['HTTP_X_FORWARDED_FOR']);
		}
		if( in_array( 'fix_adress', $this->_security ) && isset( $_SERVER['REMOTE_ADDR'] ) )
		{
			$ip	= $this->get( 'session.client.address' );
			if( $ip === null ) {
				$this->set( 'session.client.address', $_SERVER['REMOTE_ADDR'] );
			}
			else if( $_SERVER['REMOTE_ADDR'] !== $ip )
			{
				$this->_state	=	'error';
				return false;
			}
		}
		if( in_array( 'fix_browser', $this->_security ) && isset( $_SERVER['HTTP_USER_AGENT'] ) )
		{
			$browser = $this->get( 'session.client.browser' );
			if( $browser === null ) {
				$this->set( 'session.client.browser', $_SERVER['HTTP_USER_AGENT']);
			}
			else if( $_SERVER['HTTP_USER_AGENT'] !== $browser )
			{
				$this->_state	=	'error';
				return false;
			}
		}
		return true;
	}
}
?>